home *** CD-ROM | disk | FTP | other *** search
/ Windows Expert / Windows Expert.iso / windownt / uupc11ys.zip / MAIL / MAILSEND.C < prev    next >
C/C++ Source or Header  |  1993-04-10  |  36KB  |  1,003 lines

  1. /*--------------------------------------------------------------------*/
  2. /*    m a i l s e n d . c                                             */
  3. /*                                                                    */
  4. /*    Subroutines for sending mail for UUPC/extended                  */
  5. /*                                                                    */
  6. /*    Changes copyright 1990, Andrew H. Derbyshire                    */
  7. /*                                                                    */
  8. /*    Change History:                                                 */
  9. /*                                                                    */
  10. /*    15 Sep 90   - Create from maillib.c                         ahd */
  11. /*--------------------------------------------------------------------*/
  12.  
  13. #include <ctype.h>
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <string.h>
  17. #include <sys/types.h>
  18. #include <sys/stat.h>
  19. #include <process.h>
  20.  
  21. #include "arpadate.h"
  22. #include "expath.h"
  23. #include "lib.h"
  24. #include "hlib.h"
  25. #include "mlib.h"
  26. #include "alias.h"
  27. #include "mail.h"
  28. #include "maillib.h"
  29. #include "mailblib.h"
  30. #include "mailsend.h"
  31. #include "safeio.h"
  32. #include "address.h"
  33.  
  34. #define  INDENT "> "
  35.  
  36. /*--------------------------------------------------------------------*/
  37. /*                     Local function prototypes                      */
  38. /*--------------------------------------------------------------------*/
  39.  
  40.  static char *ExplodeAlias(char *header ,
  41.                       const char *alias,
  42.                       FILE *stream,
  43.                       const boolean resent);
  44.  
  45.  
  46.  static void PutHead( const char *label,
  47.                       const char *operand,
  48.                       FILE *stream,
  49.                       const boolean resent);
  50.  
  51.  static boolean Append_Signature(FILE *mailbag,
  52.                      const boolean alternate);
  53.  
  54.  static void Prompt_Input( char *tmailbag,
  55.                           FILE *fmailbag,
  56.                            char *subject,
  57.                           const int current);
  58.  
  59.  static boolean Subcommand( char *buf,
  60.                            FILE *fmailbag,
  61.                            char *tmailbag,
  62.                            char *subject,
  63.                            const int current_msg);
  64.  
  65.  static void CopyOut( const char* input);
  66.  
  67.  static void filter( char *tmailbag, char *command);
  68.  
  69.  static char *GetString( char *input);
  70.  
  71. currentfile();                /* Define current file for panic()     */
  72.  
  73. /*--------------------------------------------------------------------*/
  74. /*    E x p l o d e A l i a s                                         */
  75. /*                                                                    */
  76. /*    Resolves an alias, exploding it into a list if needed.          */
  77. /*--------------------------------------------------------------------*/
  78.  
  79.  static char *ExplodeAlias(char *header ,
  80.                       const char *alias,
  81.                       FILE *stream,
  82.                       const boolean resent)
  83. {
  84.    char *fullname;
  85.    char buffer[LSIZE];
  86.  
  87.    if ((alias == NULL) || (strlen(alias) == 0))
  88.    {
  89.       printmsg(0,"ExplodeAlias: NULL or empty string for argument");
  90.       panic();
  91.    }
  92.  
  93.    fullname = AliasByNick(alias);
  94.  
  95.    printmsg(4,"Processing alias '%s', result '%s'", alias,
  96.       (fullname == NULL) ? alias : fullname);
  97.  
  98.    if (fullname == NULL)            /* No alias found for user?     */
  99.    {                                /* No --> Try node lookup       */
  100.       char user[MAXADDR];
  101.       char node[MAXADDR];
  102.       char path[MAXADDR];
  103.       char bucket[MAXADDR];
  104.  
  105.       ExtractAddress(bucket, (char *) alias, FALSE);
  106.       user_at_node(bucket, path, node, user);
  107.       fullname = AliasByAddr( node, user);
  108.  
  109.       if (fullname == NULL)         /* Did we come up empty?         */
  110.       {
  111.          char *hisuser, *hisnode;
  112.  
  113.          hisuser = strtok( bucket, "@");
  114.          hisnode = strtok( NULL, "@");
  115.          if ((*bucket != '@') &&
  116.              equal(hisuser, user ) && (hisnode != NULL) &&
  117.              equal(hisnode, node ) && (strchr( node, '.') == NULL))
  118.          {
  119.             if (equal(hisnode, E_nodename))
  120.                strcpy(node, E_fdomain);
  121.             else {
  122.                strcat(node,".");
  123.                strcat(node,E_localdomain);
  124.             }
  125.  
  126.             ExtractAddress(path, (char *) alias, TRUE);
  127.             if (strlen( path ) == 0)
  128.                sprintf(buffer,"%s@%s", hisuser, node );
  129.             else
  130.                sprintf(buffer,"\"%s\" <%s@%s>", path, hisuser, node);
  131.             fullname = buffer;
  132.          }
  133.          else
  134.             fullname = (char *) alias; /* Use original information      */
  135.       }
  136.    }
  137.    else {
  138.       ExtractAddress(buffer,fullname,TRUE);
  139.       if (strlen(buffer) == 0)      /* A list of users?              */
  140.       {                             /* Yes --> Do recursive call     */
  141.          char *current = buffer;    /* Current token being processed */
  142.          char *next = NULL;         /* Next token to process         */
  143.  
  144.          strcpy(buffer,fullname);
  145.  
  146.          do {
  147.             current = strtok(current,",\t "); /* Get next alias to process */
  148.             next    = strtok(NULL,"");    /* Also save rest of list        */
  149.             header  = ExplodeAlias( header , current, stream, resent);
  150.                                           /* Get alias, including sub-list */
  151.             current  = next;
  152.          } while ( next != NULL );        /* Until no more tokens exist    */
  153.  
  154.          return header;                   /* Have written header, return   */
  155.  
  156.       } /* if */
  157.    } /* else */
  158.  
  159.    if (strpbrk(fullname,"!@") == nil(char))
  160.    {
  161.       sprintf(buffer,"%s@%s", fullname , E_fdomain);
  162.                               /* Local address                    */
  163.       fullname = buffer;      /* Write out the formatted address  */
  164.    }
  165.  
  166.    PutHead(header, fullname, stream, resent);
  167.                               /* Remote address                   */
  168.    return "";                 /* Make header empty string for
  169.                                  next caller                      */
  170. } /* ExplodeAlias */
  171.  
  172. /*--------------------------------------------------------------------*/
  173. /*    A p p e n d _ S i g n a t u r e                                 */
  174. /*                                                                    */
  175. /*    Append the signature file to the specified mailbag file         */
  176. /*                                                                    */
  177. /*    [Broke this code out from Send_Mail to support the ~a mail      */
  178. /*    subcommand]                                                     */
  179. /*                                                                    */
  180. /*    Returns:  0 on success, 1 if signature file not found           */
  181. /*--------------------------------------------------------------------*/
  182.  
  183. static boolean Append_Signature(FILE *mailbag_fp ,
  184.                      const boolean alternate)
  185. {
  186.    FILE *sigfp;
  187.    char *sig;
  188.    char sigfile[FILENAME_MAX];
  189.    char buf[BUFSIZ];
  190.  
  191.    sig = alternate ? E_altsignature : E_signature;
  192.  
  193.    if(sig != nil(char)) {
  194.       mkfilename(sigfile, E_homedir, sig);
  195.       printmsg(4, "Append_Signature: signature file %s", sigfile);
  196.       if ((sigfp = FOPEN(sigfile, "r",TEXT_MODE)) != nil(FILE)) {
  197.          fputs("-- \n", mailbag_fp);
  198.          while (fgets(buf, BUFSIZ, sigfp) != nil(char))
  199.             fputs(buf, mailbag_fp);
  200.          fclose(sigfp);
  201.          return(0);
  202.       }
  203.       else {
  204.          printmsg(0, "Signature file \"%s\" doesn't exist!\n", sigfile);
  205.          return(1);
  206.       }
  207.    }
  208.    return(0);
  209. }  /* Append_Signature */
  210.  
  211. /*--------------------------------------------------------------------*/
  212. /*    S e n d _ M a i l                                               */
  213. /*                                                                    */
  214. /*    Send text in a mailbag file to address(es) specified by line.   */
  215. /*--------------------------------------------------------------------*/
  216.  
  217. boolean Send_Mail(FILE *datain,
  218.                int argc,
  219.                char *argv[],
  220.                char *subject,
  221.                const boolean resent)
  222. {
  223.    int argx = 0;
  224.    char buf[LSIZE];
  225.    char *header    = "To:";
  226.    char *CcHeader  = "Cc:";
  227.    char *BccHeader = "Bcc:";
  228.    char *pipename  = mktempname(NULL, "TMP");
  229.    FILE *stream = FOPEN(pipename , "w",TEXT_MODE);
  230.    int status;
  231.  
  232. /*--------------------------------------------------------------------*/
  233. /*                     Verify our workfile opened                     */
  234. /*--------------------------------------------------------------------*/
  235.  
  236.    if ( stream == NULL )
  237.    {
  238.       printerr(pipename);
  239.       free(pipename);
  240.       return FALSE;
  241.    }
  242.  
  243. /*--------------------------------------------------------------------*/
  244. /*    Add the boilerplate the front:                                  */
  245. /*                                                                    */
  246. /*       Date, From, Organization, and Reply-To                       */
  247. /*--------------------------------------------------------------------*/
  248.  
  249.    PutHead("Date:", arpadate() , stream, resent);
  250.  
  251.    if (bflag[F_BANG])
  252.       sprintf(buf, "(%s) %s!%s", E_name, E_fdomain, E_mailbox );
  253.    else
  254.       sprintf(buf, "\"%s\" <%s@%s>", E_name, E_mailbox, E_fdomain );
  255.    PutHead("From:", buf, stream , resent);
  256.  
  257.    if (E_organization != NULL )
  258.       PutHead("Organization:", E_organization, stream, resent );
  259.  
  260.    if (E_replyto != NULL )
  261.    {
  262.       if (strpbrk(E_replyto,"!@") == nil(char))
  263.          sprintf(buf,"\"%s\" <%s@%s>", E_name, E_replyto , E_fdomain);
  264.       else
  265.          sprintf(buf,"\"%s\" <%s>", E_name, E_replyto);
  266.       PutHead("Reply-To:", buf, stream, resent );
  267.    }
  268.  
  269. /*--------------------------------------------------------------------*/
  270. /*                      Write the addressees out                      */
  271. /*--------------------------------------------------------------------*/
  272.  
  273.    for (argx = 0 ; argx < argc; argx++ )
  274.    {
  275.       if (equal(argv[argx],"-c"))
  276.       {
  277.          header = CcHeader;
  278.          CcHeader = "";
  279.       } /* if */
  280.       else if (equal(argv[argx],"-b"))
  281.       {
  282.          header = BccHeader;
  283.          CcHeader = BccHeader = "";
  284.       } /* if else */
  285.       else
  286.          header = ExplodeAlias( header , argv[argx], stream, resent);
  287.    } /* for */
  288.  
  289. /*--------------------------------------------------------------------*/
  290. /*                  Prompt for carbon copies, if any                  */
  291. /*--------------------------------------------------------------------*/
  292.  
  293.    if ( bflag[F_ASKCC] && Is_Console(stdin) &&
  294.         Console_fgets(buf,LSIZE,"Cc: "))
  295.    {
  296.       char *current = buf;
  297.       header = CcHeader;
  298.       CcHeader = "";
  299.       printmsg(4,"CC buffer: %s",current);
  300.  
  301.       while ((current != NULL) &&
  302.              (current = strtok(current,",\t\n ")) != NULL)
  303.       {
  304.          char *next  =  strtok(NULL,"");
  305.          if (equal(current,"-b"))
  306.          {
  307.             header = BccHeader;
  308.             CcHeader = BccHeader = "";
  309.          } /* if */
  310.          else
  311.             header = ExplodeAlias( header, current, stream, resent);
  312.          current = next;
  313.       } /* while */
  314.    }  /* If Console_fgets() */
  315.  
  316. /*--------------------------------------------------------------------*/
  317. /*                     Handle the subject, if any                     */
  318. /*--------------------------------------------------------------------*/
  319.  
  320.    if (subject != NULL)
  321.       PutHead("Subject:",subject, stream, resent);
  322.    PutHead(NULL, "", stream, resent);  /* Terminate the header file   */
  323.  
  324. /*--------------------------------------------------------------------*/
  325. /*                    Copy the body of the message                    */
  326. /*--------------------------------------------------------------------*/
  327.  
  328.    while (fgets(buf, LSIZE, datain) != nil(char))
  329.    {
  330.       int result = fputs(buf, stream );
  331.       if (result == EOF)
  332.       {
  333.          printerr( pipename );
  334.          panic();
  335.       } /* if */
  336.       if (buf[strlen(buf)-1] != '\n')
  337.             fputc('\n', stream);
  338.    } /* while */
  339.  
  340.    if (!feof(datain))
  341.    {
  342.       printerr("Send_Mail:");
  343.       panic();
  344.    } /* if */
  345.  
  346.    if (datain != stdin)
  347.       fclose(datain);
  348.  
  349. /*--------------------------------------------------------------------*/
  350. /*    Append user's primary signature file, if autosign option on     */
  351. /*--------------------------------------------------------------------*/
  352.  
  353.    if ( bflag[F_AUTOSIGN] )
  354.       Append_Signature(stream, FALSE);
  355.  
  356.    fclose(stream);
  357.  
  358. /*--------------------------------------------------------------------*/
  359. /*                  Invoke the mail delivery program                  */
  360. /*--------------------------------------------------------------------*/
  361.  
  362.    if ( freopen( pipename, "r" , stdin) == NULL )
  363.    {
  364.       printerr(CONSOLE);
  365.       return FALSE;
  366.    }
  367.    else {
  368.       status = spawnlp( P_WAIT, RMAIL,RMAIL, "-t", NULL);
  369.       if ( status < 0 )
  370.       {
  371.          printerr( RMAIL );
  372.          printmsg(0,"Unable to execute rmail; mail not delivered.");
  373.       }
  374.       else if ( status > 0 )
  375.          printmsg(0,
  376.             "rmail returned non-zero status; delivery may be incomplete.");
  377.    } /* else */
  378.  
  379.    freopen( CONSOLE, "r", stdin);
  380.  
  381. /*--------------------------------------------------------------------*/
  382. /*               Log a copy of the mail for the sender                */
  383. /*--------------------------------------------------------------------*/
  384.  
  385.    if (bflag[F_SAVERESENT] || ! resent)
  386.       CopyOut(pipename);
  387.  
  388. /*--------------------------------------------------------------------*/
  389. /*                   Clean up and return to caller                    */
  390. /*--------------------------------------------------------------------*/
  391.  
  392.    remove(pipename);
  393.    free(pipename);
  394.    return (status == 0 );
  395.  
  396. } /*Send_Mail*/
  397.  
  398. /*--------------------------------------------------------------------*/
  399. /*    C o p y O u t                                                   */
  400. /*                                                                    */
  401. /*    Save copy of outgoing mail, if desired                          */
  402. /*--------------------------------------------------------------------*/
  403.  
  404. static void CopyOut( const char* input)
  405. {
  406.    FILE *datain;
  407.    FILE *dataout;
  408.    char buf[BUFSIZ];
  409.    char outbox[FILENAME_MAX];
  410.  
  411.    if (E_filesent == NULL)
  412.       return;
  413.  
  414.    strcpy( outbox, E_filesent);
  415.    expand_path( outbox, E_homedir, E_homedir, E_mailext );
  416.  
  417.    datain = FOPEN( input, "r",TEXT_MODE);
  418.  
  419.    if (datain == NULL )
  420.    {
  421.       printerr(input);
  422.       panic();
  423.    } /* if */
  424.  
  425.    dataout = FOPEN( outbox, "a",TEXT_MODE);
  426.    if (dataout == NULL )
  427.    {
  428.       printerr( outbox );
  429.       panic();
  430.    } /* if */
  431.  
  432.    fputs(MESSAGESEP,dataout);
  433.  
  434.    while (fgets(buf, BUFSIZ, datain) != nil(char))
  435.    {
  436.       int result = fputs(buf, dataout);
  437.       if (result == EOF)
  438.       {
  439.          printerr( outbox );
  440.          panic();
  441.       } /* if */
  442.    } /* while */
  443.  
  444.    if (!feof(datain))
  445.    {
  446.       printerr(input);
  447.       panic();
  448.    } /* if */
  449.  
  450.    fclose(datain);
  451.    fclose(dataout);
  452. } /* CopyOut */
  453.  
  454. /*--------------------------------------------------------------------*/
  455. /* P u t H e a d                                                      */
  456. /*                                                                    */
  457. /* Write one line of an RFC-822 header                                */
  458. /*--------------------------------------------------------------------*/
  459.  
  460.  static void PutHead( const char *label,
  461.                       const char *operand,
  462.                       FILE *stream,
  463.                       const boolean resent)
  464.  {
  465.    static boolean terminate = TRUE;
  466.                               /* If previous line was terminated     */
  467.  
  468.    if (label == NULL )        /* Terminate call?                     */
  469.    {                          /* Yes --> Reset Flag and return       */
  470.       fputc('\n', stream);    /* Terminate the current line          */
  471.       if (!resent)
  472.          fputc('\n', stream); /* Terminate the header file           */
  473.       terminate = TRUE;
  474.       return;
  475.    } /* if */
  476.  
  477.    if (strlen(label))         /* First line of a header?             */
  478.    {
  479.       if (!terminate)         /* Terminate previous line?            */
  480.          fputc('\n', stream);
  481.       if (resent)
  482.          fprintf(stream,"Resent-%s %s",label, operand);
  483.       else
  484.          fprintf(stream,"%-10s %s",label, operand);
  485.       terminate = FALSE;          /* Flag that we did not end file   */
  486.    } /* if */
  487.    else                       /* Continuing line                     */
  488.       fprintf(stream,",\n%-10s %s",label, operand);
  489.  } /* PutHead */
  490.  
  491. /*--------------------------------------------------------------------*/
  492. /*    C o l l e c t _ M a i l                                         */
  493. /*                                                                    */
  494. /*    Create mailbox file for delivery                                */
  495. /*--------------------------------------------------------------------*/
  496.  
  497. boolean Collect_Mail(FILE *stream,
  498.                   int argc,
  499.                   char **argv,
  500.                   const int current_msg,
  501.                   const boolean reply)
  502. {
  503.    boolean editonly = FALSE;
  504.    char Subuffer[LSIZE];
  505.    char *subject = NULL;
  506.    char *tmailbag;
  507.    int  c;                    /* Really a 'char', but who cares?     */
  508.    boolean done = FALSE;
  509.    FILE  *fmailbag;
  510.  
  511.  
  512. /*--------------------------------------------------------------------*/
  513. /*      Determine if we are running interactively; if not, just       */
  514. /*         pass the input stream to Send_Mail for processing          */
  515. /*--------------------------------------------------------------------*/
  516.  
  517.    if (!Is_Console(stream))
  518.    {
  519.       if ( equal(argv[0], "-s" ) )
  520.          return Send_Mail( stream, argc-2, argv+2, argv[1], FALSE);
  521.       else
  522.          return Send_Mail( stream , argc , argv, NULL , FALSE);
  523.    } /* if */
  524.  
  525. /*--------------------------------------------------------------------*/
  526. /*    We are running interactively; create a determine the name of    */
  527. /*                        our work file to be.                        */
  528. /*--------------------------------------------------------------------*/
  529.  
  530.    *Subuffer = '\0';          /* Assume no subject */
  531.    tmailbag = mktempname( NULL, "TXT");
  532.  
  533. /*--------------------------------------------------------------------*/
  534. /*         Determine if we should go straight into the editor         */
  535. /*--------------------------------------------------------------------*/
  536.  
  537.    editonly = bflag[F_AUTOEDIT] && (E_editor != NULL);
  538.  
  539.    if ( equal(argv[0],"-s"))     /* Any subject specified?           */
  540.    {
  541.       strcpy( Subuffer , argv[1] ); /* Save the subject for later    */
  542.       argv += 2;                 /* Skip over it in argument list    */
  543.       argc -= 2;                 /* Giving us fewer args left        */
  544.    }
  545.    else {                        /* No --> Prompt for one            */
  546.       if(Console_fgets(Subuffer,LSIZE,"Subject: "))
  547.       {
  548.          if (Subuffer[strlen(Subuffer) - 1 ] == '\n')
  549.             Subuffer[strlen(Subuffer)-1] = '\0';   /* End the subject */
  550.       }  /* If Console_fgets() */
  551.    } /* if ( equal(argv[0],"-s")) */
  552.  
  553. /*--------------------------------------------------------------------*/
  554. /*      Copy a message from the original input to temporary file      */
  555. /*--------------------------------------------------------------------*/
  556.  
  557.    fmailbag = FOPEN(tmailbag, "w",TEXT_MODE);
  558.    if (fmailbag == NULL )
  559.    {
  560.       printerr( tmailbag );
  561.       panic();
  562.    }
  563.  
  564. /*--------------------------------------------------------------------*/
  565. /*               Include incoming message if requested                */
  566. /*--------------------------------------------------------------------*/
  567.  
  568.    if ( bflag[F_AUTOINCLUDE] && reply)
  569.    {
  570.        CopyMsg(current_msg, fmailbag, fromheader , TRUE);
  571.        fprintf(stdout, "Message %d Included\n", current_msg+1);
  572.    } /* if ( bflag[F_AUTOINCLUDE] && reply) */
  573.  
  574. /*--------------------------------------------------------------------*/
  575. /*                     Edit the file if requested                     */
  576. /*--------------------------------------------------------------------*/
  577.  
  578.    if (editonly)              /* Enter editor immediately?     ahd   */
  579.    {                          /* Yes --> Go to it                    */
  580.       fclose(fmailbag);
  581.       Invoke_Editor(E_editor, tmailbag);
  582.    } /* if */
  583.    else {                     /* No  --> prompt for data       ahd   */
  584.       Prompt_Input( tmailbag , fmailbag , Subuffer, current_msg );
  585.       fclose(fmailbag);
  586.    } /*else */
  587.  
  588.  
  589.    do {
  590.       fputs("\nAbort, Continue, Edit, List, or Send? ",stdout);
  591.       c     = Get_One();             /* adaptation for QuickC */  /* pdm */
  592.       switch (tolower( c ))
  593.       {
  594.          case 'c':
  595.             puts("Continue");
  596.             fmailbag = FOPEN(tmailbag, "a",TEXT_MODE);
  597.             Prompt_Input( tmailbag , fmailbag , Subuffer, current_msg );
  598.             fclose(fmailbag);
  599.             break;
  600.  
  601.          case 'l':
  602.             puts("List");
  603.             Sub_Pager(tmailbag, islower(c) );
  604.             break;
  605.  
  606.          case 's':
  607.             puts("Send");
  608.             fmailbag = FOPEN(tmailbag, "r",TEXT_MODE);
  609.             if (fmailbag == NULL )
  610.             {
  611.                printerr(tmailbag);
  612.                panic();
  613.             }
  614.             if ( strlen( Subuffer ))
  615.                subject = Subuffer;
  616.             Send_Mail(fmailbag, argc, argv, subject, FALSE);
  617.             done = TRUE;
  618.             break;
  619.  
  620.          case 'e':
  621.             puts("Edit");
  622.             Invoke_Editor(E_editor, tmailbag);
  623.             break;
  624.  
  625.          case 'a':
  626.             fputs("Abort\nAre you sure? ", stdout);
  627.             safeflush();
  628.             c = Get_One();             /* for QuickC */          /* pdm */
  629.             switch (tolower(c)) {      /* unravel these two calls */
  630.             case 'y':
  631.                puts("Yes");
  632.                done = TRUE;
  633.                break;
  634.             default:
  635.                puts("No");
  636.             } /*switch*/
  637.             break;
  638.  
  639.          default:
  640.             puts("\n\aEnter A, C, E, L, or S.");
  641.             safeflush();
  642.             done = FALSE;
  643.       } /*switch*/
  644.    } while (!done);
  645.  
  646.    remove(tmailbag);
  647.    free(tmailbag);
  648.  
  649.    return TRUE;
  650. } /*Collect_Mail*/
  651.  
  652.  
  653. /*--------------------------------------------------------------------*/
  654. /*    P r o m p t _ I n p u t                                         */
  655. /*                                                                    */
  656. /*    Prompt for mail entry interactively.                            */
  657. /*--------------------------------------------------------------------*/
  658.  
  659. static void Prompt_Input( char *tmailbag,
  660.             FILE *fmailbag,
  661.             char *subject,
  662.             const int current_msg)
  663. {
  664.    char buf[LSIZE];
  665.  
  666.    printf("\nEnter message.  Enter ~? for help.  End input with %s\n",
  667.           bflag[ F_DOT ] ?  "a period (.)" :
  668.          "end-of-file (Control-Z)");
  669.    for ( ; ; )
  670.    {
  671.       if (Console_fgets(buf, LSIZE, "? "))
  672.       {
  673.          if (bflag[F_DOT] && equal(buf,".\n"))
  674.             break;
  675.          else if (Subcommand( buf, fmailbag, tmailbag, subject, current_msg) )
  676.             continue;      /*Don't write line out if subcommand   */
  677.       } /* if */
  678.       else
  679.          break;            /* Exit loop if end of file            */
  680.  
  681.       if (fputs(buf, fmailbag) == EOF )
  682.       {
  683.          printerr( tmailbag );
  684.          panic();
  685.       } /* if (fputs(buf, fmailbag) == EOF ) */
  686.  
  687.       if (buf[strlen(buf)-1] != '\n')
  688.          fputc('\n', fmailbag);
  689.    } /* for */
  690. } /* Prompt_Input */
  691.  
  692. /*--------------------------------------------------------------------*/
  693. /*    S u b c o m m a n d                                             */
  694. /*                                                                    */
  695. /*    Handle tilde (~) subcommands for Interactive mail               */
  696. /*--------------------------------------------------------------------*/
  697.  
  698. static boolean Subcommand( char *buf,
  699.                            FILE *fmailbag,
  700.                            char *tmailbag,
  701.                            char *subject,
  702.                            const int current_msg)
  703. {
  704.    int message;
  705.    char fname[FILENAME_MAX];
  706.    char *token;
  707.    FILE *stream;
  708.  
  709.    if(*buf == '~')        /* Handle mail subcommands  pdm */
  710.    {
  711.       switch(buf[1])
  712.       {
  713.  
  714. /*--------------------------------------------------------------------*/
  715. /*                     Treat as normal data line                      */
  716. /*--------------------------------------------------------------------*/
  717.  
  718.          case '~':
  719.             memmove( buf, buf + 1, strlen( buf + 1 ));
  720.             return FALSE;        // Treat as normal line
  721.  
  722. /*--------------------------------------------------------------------*/
  723. /*              Put signature file into current message               */
  724. /*--------------------------------------------------------------------*/
  725.  
  726.          case 'a':
  727.          case 'A':
  728.             Append_Signature(fmailbag, isupper( buf[1] ));
  729.             fputs("(continue)\n", stdout);
  730.             break;
  731.  
  732. /*--------------------------------------------------------------------*/
  733. /*                       Edit outgoing message                        */
  734. /*--------------------------------------------------------------------*/
  735.  
  736.          case 'v':            /* UNIX allows 'v'isual editor         */
  737.          case 'e':
  738.             /* invoke editor with current msg */
  739.             fclose(fmailbag);
  740.             Invoke_Editor(E_editor, tmailbag);
  741.             fmailbag = FOPEN(tmailbag, "a",TEXT_MODE);
  742.             fputs("(continue)\n", stdout);
  743.             break;
  744.  
  745. /*--------------------------------------------------------------------*/
  746. /*                 Include any letter in this message                 */
  747. /*--------------------------------------------------------------------*/
  748.  
  749.          case 'f':
  750.          case 'F':
  751.          case 'i':
  752.          case 'I':
  753.          case 'm':
  754.          case 'M':
  755.             if (fmailbox == NULL)
  756.                puts("Mailbox not accessible!");
  757.             else {
  758.                int *item_list;
  759.                int next_item = PushItemList( &item_list );
  760.                boolean first_pass = TRUE;
  761.  
  762.                token = GetString( &buf[2] );
  763.  
  764.                if (SelectItems( &token, current_msg, LETTER_OP ))
  765.                while( Get_Operand( &message, &token, LETTER_OP, first_pass))
  766.                {
  767.                   CopyMsg( message , fmailbag,
  768.                            islower(buf[1]) ? fromheader : noseperator ,
  769.                            tolower(buf[1]) != 'f');
  770.                   fprintf(stdout, "Message %d included\n", message + 1);
  771.                   first_pass = FALSE;
  772.                } /* while */
  773.  
  774.                PopItemList( item_list, next_item );
  775.             } /* else */
  776.             break;
  777.  
  778. /*--------------------------------------------------------------------*/
  779. /*                       Print current message                        */
  780. /*--------------------------------------------------------------------*/
  781.  
  782.          case 'p':
  783.          case 'P':
  784.             fclose(fmailbag);
  785.             Sub_Pager(tmailbag, islower(buf[1]) );
  786.             fmailbag = FOPEN(tmailbag, "a",TEXT_MODE);
  787.             fputs("(continue)\n", stdout);
  788.             break;
  789.  
  790. /*--------------------------------------------------------------------*/
  791. /*                           Include a file                           */
  792. /*--------------------------------------------------------------------*/
  793.  
  794.          case 'r':
  795.             token = strtok( &buf[2], " \t\n");
  796.             if ( token == NULL )
  797.             {
  798.                printf("Need a file name for this command!\n");
  799.                break;
  800.             }
  801.             strcpy( fname, token );
  802.             if ( expand_path( fname, NULL, E_homedir , NULL) == NULL )
  803.                break;
  804.             stream = FOPEN( fname, "r",TEXT_MODE);
  805.             if (stream == NULL )
  806.                printerr(fname);
  807.             else while( fgets( buf, LSIZE, stream ))
  808.             {
  809.                fputs( buf, fmailbag);
  810.                if ferror( fmailbag )
  811.                {
  812.                   printerr( tmailbag);
  813.                   break;
  814.                } /* if */
  815.             } /* else while */
  816.             if (ferror( stream ) )
  817.             {
  818.                printerr( fname );
  819.                clearerr( stream );
  820.             } /* if */
  821.             fclose( stream );
  822.             fputs("(continue)\n", stdout);
  823.             break;
  824.  
  825.  
  826. /*--------------------------------------------------------------------*/
  827. /*                        Change mail subject                         */
  828. /*--------------------------------------------------------------------*/
  829.  
  830.          case 's':
  831.             token = GetString( &buf[2] );
  832.             if ( token != NULL )
  833.             {
  834.                strcpy( subject, token );
  835.             }
  836.             else
  837.                printf("No new subject, command ignored\n");
  838.             printf("Subject: %s\n",subject);
  839.             break;
  840.  
  841. /*--------------------------------------------------------------------*/
  842. /*                                Help                                */
  843. /*--------------------------------------------------------------------*/
  844.  
  845.          case '?':
  846.          {
  847.             mkfilename(fname, E_confdir, "tilde.hlp");
  848.             Sub_Pager( fname, TRUE );
  849.             break;
  850.          }
  851.  
  852. /*--------------------------------------------------------------------*/
  853. /*                             A subshell                             */
  854. /*--------------------------------------------------------------------*/
  855.  
  856.          case '!':
  857.             token = strtok( &buf[2], "\n");
  858.             subshell( token );
  859.             break;
  860.  
  861. /*--------------------------------------------------------------------*/
  862. /*                     Pipe mail through a filter                     */
  863. /*--------------------------------------------------------------------*/
  864.  
  865.          case '|':
  866.             fclose( fmailbag );
  867.             filter( tmailbag, &buf[2] );
  868.             fmailbag = FOPEN(tmailbag, "a",TEXT_MODE);
  869.             fputs("(continue)\n", stdout);
  870.             break;
  871.  
  872. /*--------------------------------------------------------------------*/
  873. /*                          Invalid command                           */
  874. /*--------------------------------------------------------------------*/
  875.  
  876.          default:
  877.             fputs("Unknown mail subcommand, ~? for help.\n",
  878.                   stdout);
  879.             break;
  880.       } /* switch */
  881.  
  882.       return TRUE;
  883.    } /* if */
  884.    else
  885.       return FALSE;           /* It wasn't a sub-command             */
  886.  
  887. } /*SubCommand*/
  888.  
  889. /*--------------------------------------------------------------------*/
  890. /*    f i l t e r                                                     */
  891. /*                                                                    */
  892. /*    Filter the next of an outgoing program into the output mail     */
  893. /*--------------------------------------------------------------------*/
  894.  
  895. static void filter( char *tmailbag, char *command)
  896. {
  897.    char pipename[FILENAME_MAX];
  898.    char *argv[50];
  899.    struct stat statbuf;
  900.    int    argc;
  901.    int    result = 0;
  902.  
  903. /*--------------------------------------------------------------------*/
  904. /*          Break the command to execute down into arguments          */
  905. /*--------------------------------------------------------------------*/
  906.  
  907.    argc = getargs(command, argv);
  908.  
  909.    if ( argc == 0 )
  910.    {
  911.       printf("No filter name given!\n");
  912.       return;
  913.    }
  914.    argv[argc] = NULL;
  915.  
  916. /*--------------------------------------------------------------------*/
  917. /*   Set up our standard input and standard output for the command    */
  918. /*--------------------------------------------------------------------*/
  919.  
  920.    mktempname(pipename, "TXT");
  921.  
  922.    if ( freopen(tmailbag, "r", stdin) == NULL )
  923.       printerr( tmailbag );
  924.    else if (freopen(pipename, "w", stdout) == NULL )
  925.    {
  926.       printerr( pipename );
  927.       freopen("con", "r", stdin);
  928.    }
  929. /*--------------------------------------------------------------------*/
  930. /*                          Run the command                           */
  931. /*--------------------------------------------------------------------*/
  932.    else {
  933.       result = spawnvp( P_WAIT, argv[0], argv );
  934.       freopen("con", "w", stdout);
  935.       setbuf(stdout, NULL );
  936.       freopen("con", "r", stdin);
  937.       setbuf(stdin, NULL );
  938.  
  939.       if (result == -1)       /* Did spawn fail?            */
  940.          printerr(argv[0]);   /* Yes --> Report error       */
  941.       else if( stat( pipename, &statbuf) <0 )   /* Create output?    */
  942.       {
  943.          printf(0,"Cannot determine status of output %s",pipename);
  944.          printerr( pipename );
  945.       }
  946.       else if( statbuf.st_size == 0 )  /* Anything in the file?      */
  947.          printf("Outfile file is empty!\n");
  948.       else {                  /* Good output, replace input file     */
  949.          remove( tmailbag );
  950.          if (rename( pipename, tmailbag ))
  951.             printerr( pipename );
  952.       } /* else */
  953.    } /* else */
  954.  
  955. /*--------------------------------------------------------------------*/
  956. /*                   Clean up and return to caller                    */
  957. /*--------------------------------------------------------------------*/
  958.  
  959.    remove( pipename );
  960.  
  961. } /* filter */
  962.  
  963. /*--------------------------------------------------------------------*/
  964. /*    G e t S t r i n g                                               */
  965. /*                                                                    */
  966. /*    Get non-whitespace in a string                                  */
  967. /*--------------------------------------------------------------------*/
  968.  
  969. static char *GetString( char *input)
  970. {
  971.    char *end;
  972.  
  973. /*--------------------------------------------------------------------*/
  974. /*                   Look for first data in string                    */
  975. /*--------------------------------------------------------------------*/
  976.  
  977.    while( isspace( *input ))
  978.       input++ ;
  979.  
  980. /*--------------------------------------------------------------------*/
  981. /*   If no input or all blanks, return NULL to denote empty string    */
  982. /*--------------------------------------------------------------------*/
  983.  
  984.    if (*input == '\0')
  985.       return NULL ;
  986.  
  987. /*--------------------------------------------------------------------*/
  988. /*                Delete whitespace from end of string                */
  989. /*--------------------------------------------------------------------*/
  990.  
  991.    end = &input[ strlen( input ) - 1 ];
  992.  
  993.    while (isspace(*end))
  994.       *end-- = '\0';
  995.  
  996. /*--------------------------------------------------------------------*/
  997. /*                 Return beginning of string to caller               */
  998. /*--------------------------------------------------------------------*/
  999.  
  1000.    return input;
  1001.  
  1002. } /* GetString */
  1003.